// book.js 1.2.0
// http://www.opensource.org/licenses/mit-license.php
// http://www.1xiezuo.com
// Copyright (c) 2014-2019 1xiezuo.com
// a parsor for scripts

; (function () {
    'use strict';

    var regex = {

        block: /@@(第(\d*)[场|节|聊] *(.*))/,
        taxonomy: /(第\d*([章|卷|集])) *(.*)/,

        action: /^(.+)/g,
        centered: /^(?:> *)(.+)(?: *<)(\n.+)*/g,

        //script
        transition: /^(?:.*)((淡入|切至|切回|淡出)[:：])/,
        dialogue: /^(.{0,20})(\^?)?[:：]+(?!\/)(?:\n?)([\s\S]+)/,
        parenthetical: /^([\(（].+[\)）])$/,
        dpicture: /^(\{{2}.+\}{2})$/,
        cover_main: /^名称[:：](.+)\n(编剧|作者)[:：](.+)(\n备注[:：](.+))?/,
        cover_notes: /^([\s\S]+)/,
        summary: /^(.+)/g,
        shortsummary: /^一句话梗概[:：](.*)/,
        refer: /^(.+)/g,
        actor: /^(.+)(?:\n?)(.*)(?:\n?)(头像[:：](.+))?/,

        note: /^(?:\[{2}(?!\[+))(.+)(?:\]{2}(?!\[+))$/,
        note_inline: /(?:\[{2}(?!\[+))([\s\S]+?)(?:\]{2}(?!\[+))/g,
        boneyard: /(^\/\*|^\*\/)$/g,

        page_break: /^\={3,}$/,
        line_break: /^ {2}$/,

        emphasis: /(_|\*{1,3}|_\*{1,3}|\*{1,3}_)(.+)(_|\*{1,3}|_\*{1,3}|\*{1,3}_)/g,
        bold_italic_underline: /(_{1}\*{3}(?=.+\*{3}_{1})|\*{3}_{1}(?=.+_{1}\*{3}))(.+?)(\*{3}_{1}|_{1}\*{3})/g,
        bold_underline: /(_{1}\*{2}(?=.+\*{2}_{1})|\*{2}_{1}(?=.+_{1}\*{2}))(.+?)(\*{2}_{1}|_{1}\*{2})/g,
        italic_underline: /(?:_{1}\*{1}(?=.+\*{1}_{1})|\*{1}_{1}(?=.+_{1}\*{1}))(.+?)(\*{1}_{1}|_{1}\*{1})/g,
        bold_italic: /(\*{3}(?=.+\*{3}))(.+?)(\*{3})/g,
        bold: /(\*{2}(?=.+\*{2}))(.+?)(\*{2})/g,
        italic: /(\*{1}(?=.+\*{1}))(.+?)(\*{1})/g,
        underline: /(_{1}(?=.+_{1}))(.+?)(_{1})/g,

        keyvalue: /(.+)[:：](.*)/,
        splitter: /\n{2,}/g,
        splitterBody: /\n{1,}/g,
        cleaner: /^\n+|\n+$/,
        standardizer: /\r/g,
        whitespacer: /^\t+|^ {3,}/gm,
        charpter: /## *(.*)\n{2,}/g,
        spdcharpter: /(?:@@|##)+ *(.*)\n{2,}/g,
        picture: /^插图[:：](.*)$/
    };

    var lexer = function (script) {
        return script.replace(regex.boneyard, '\n$1\n')
            .replace(regex.standardizer, '')
            .replace(regex.cleaner, '')
            .replace(regex.whitespacer, '');
    };

    var S4 = function () {
        return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
    };

    // Generate a pseudo-GUID by concatenating random hexadecimal.
    var guid = function () {
        return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
    };

    /**cover **/

    var tokenizeCover = function (script) {
        var src = script.split(regex.splitter)
            , i = src.length, line, match
            , tokens = [];

        while (i--) {
            line = src[i];
            // cover_main
            //var mainregex="^"+$L("名称")+"[:：](.+)\n("+$L("作者")+"[:：](.+)(\n"+$L("备注")+"[:：](.+))?";
            var mainregex = "^" + $L("名称") + "[:：](.+)\n(" + $L("编剧") + "|" + $L("作者") + ")[:：](.+)(\n" + $L("备注") + "[:：](.+))?";
            //console.log('......cover..'+mainregex);
            if (match = line.match(new RegExp(mainregex))) {
                if (match[5]) {
                    tokens.push({ type: 'cover_source', text: match[5] });
                }
                if (match[3]) {
                    tokens.push({ type: 'cover_author', text: match[3] });
                }
                tokens.push({ type: 'cover_name', text: match[1] });
                continue;
            }

            // cover_notes
            if (match = line.match(regex.cover_notes)) {
                var notes = match[0].split('\n');
                if (notes.length) {
                    tokens.push({ type: 'cover_notes_end', text: '' });
                    while (notes.length) {
                        var note = notes.pop();
                        //console.log(JSON.stringify(note));
                        var n = note.split(regex.keyvalue);
                        // console.log(n[1]);
                        switch (n[1]) {
                            case $L("时间"):
                                tokens.push({ type: 'cover_date', text: note });
                                break;
                            case $L("文体"):
                                tokens.push({ type: 'cover_booktype', text: note });
                                break;
                            case $L("展示"):
                                tokens.push({ type: 'cover_published', text: note });
                                break;
                            case $L("状态"):
                                tokens.push({ type: 'cover_status', text: note });
                                break;
                            case $L("风格"):
                                tokens.push({ type: 'cover_style', text: note });
                                break;
                            case $L("类别"):
                                tokens.push({ type: 'cover_type', text: note });
                                break;
                            case $L("长度"):
                                tokens.push({ type: 'cover_size', text: note });
                                break;
                            case $L("联系"):
                                tokens.push({ type: 'cover_contact', text: note });
                                break;
                            case $L("版权"):
                                tokens.push({ type: 'cover_copyright', text: note });
                                break;
                            case $L("阅读章显示"):
                                tokens.push({ type: 'cover_chapterdisplay', text: note });
                                break;
                            case $L("阅读节显示"):
                                tokens.push({ type: 'cover_partdisplay', text: note });
                                break;
                            default:
                                tokens.push({ type: 'cover_note', text: note });
                                break;
                        }
                    }

                    tokens.push({ type: 'cover_notes_start', text: '' });
                }
            }
        }

        return tokens;
    };

    var parseCover = function (script) {
        var tokens = tokenizeCover(script)
            , i = tokens.length, token, html = [], output;
        html.push('<div class=\"charpter\">');
        while (i--) {
            token = tokens[i];
            token.text = inline.lexer(token.text);
            switch (token.type) {
                case 'cover_author': html.push('<p class=\"cover-author\">' + token.text + '</p>'); break;
                case 'cover_name': html.push('<p class=\"cover-name\">' + token.text + '</p>'); break;
                case 'cover_source': html.push('<p class=\"cover-source\">' + token.text + '</p>'); break;
                case 'cover_notes_start': html.push('<div class=\"cover-notes\">'); break;
                case 'cover_notes_end': html.push('</div>'); break;
                case 'cover_type': html.push('<div class=\"cover-type\">' + token.text + '</div>'); break;
                case 'cover_booktype': html.push('<div class=\"cover-booktype\">' + token.text + '</div>'); break;
                case 'cover_status': html.push('<div class=\"cover-status\">' + token.text + '</div>'); break;
                case 'cover_style': html.push('<div class=\"cover-style\">' + token.text + '</div>'); break;
                case 'cover_published': html.push('<div class=\"cover-date\">' + token.text + '</div>'); break;
                case 'cover_date': html.push('<div class=\"cover-date\">' + token.text + '</div>'); break;
                case 'cover_contact': html.push('<div class=\"cover-contact\">' + token.text + '</div>'); break;
                case 'cover_size': html.push('<div class=\"cover-size\">' + token.text + '</div>'); break;
                case 'cover_copyright': html.push('<div class=\"cover-copyright\">' + token.text + '</div>'); break;
                //case 'cover_chapterdisplay': html.push('<div class=\"cover-chapterdisplay\">' + token.text + '</div>'); break;
                // case 'cover_partdisplay': html.push('<div class=\"cover-partdisplay\">' + token.text + '</div>'); break;
                case 'cover_note': html.push('<div class=\"cover-note\">' + token.text + '</div>'); break;
            }
        }
        html.push('</div>');
        output = html.join('');
        return output;
    };


    /**summary **/

    var tokenizeSummary = function (script) {
        var src = script.split(regex.splitterBody)
            , i = src.length, line, match
            , tokens = [], summary = [];

        while (i--) {
            line = src[i];
            // centered
            if (match = line.match(regex.centered)) {
                tokens.push({ type: 'centered', text: match[0].replace(/>|</g, '') });
                continue;
            }
            // shortsummary
            if (match = line.match(new RegExp("^" + $L("一句话梗概") + "[:：](.*)"))) {
                tokens.push({ type: 'shortsummary', text: match[1] });
                continue;
            }
            summary.unshift(line);
            /**
            // summary
            if (match = line.match(regex.summary)) {
              tokens.push({ type: 'summary', text: match[0]});
              continue;
            }
            // line breaks
            if (regex.line_break.test(line)) {
              tokens.push({ type: 'line_break' });
              continue;
            }**/
        }
        tokens.unshift({ type: 'summary', text: summary.join('\n\n') });
        return tokens;
    };

    /**primer **/

    var tokenizePrimer = function (script) {
        var src = script.split(regex.splitterBody)
            , i = src.length, line, match
            , tokens = [];

        while (i--) {
            line = src[i];
            // centered
            if (match = line.match(regex.centered)) {
                tokens.push({ type: 'centered', text: match[0].replace(/>|</g, '') });
                continue;
            }
            // shortsummary
            if (match = line.match(new RegExp("^" + $L("标题") + "[:：](.*)"))) {
                tokens.push({ type: 'title', text: match[1] });
                continue;
            }

            // summary
            if (match = line.match(regex.summary)) {
                tokens.push({ type: 'body', text: match[0] });
                continue;
            }
            // line breaks
            if (regex.line_break.test(line)) {
                tokens.push({ type: 'line_break' });
                continue;
            }
            tokens.push({ type: 'action', text: line });
        }
        return tokens;
    };

    var parseSummary = function (script) {
        var tokens = tokenizeSummary(script)
            , i = tokens.length, token, html = [], output;
        html.push('<div class=\"charpter\">');
        html.push('<p class=\"charpter-title\">' + $L('梗概') + '</p>');
        while (i--) {
            token = tokens[i];
            token.text = inline.lexer(token.text);
            switch (token.type) {
                case 'summary': html.push('<p class=\"action\">' + token.text + '</p>'); break;
                case 'shortsummary': html.push('<p class=\"bold action\">' + token.text + '</p>'); break;
            }
        }
        html.push('</div>');
        output = html.join('');
        return output;
    };
    var parsePrimer = function (script) {

        var tokens = tokenizePrimer(script)
            , i = tokens.length, token, html = [], output;
        html.push('<div class=\"charpter\">');
        while (i--) {
            token = tokens[i];
            token.text = inline.lexer(token.text);
            switch (token.type) {
                case 'body': html.push('<p class=\"action\">' + token.text + '</p>'); break;
                case 'title': html.push('<p class=\"charpter-title\">' + token.text + '</p>'); break;
            }
        }
        html.push('</div>');
        output = html.join('');
        //console.log('primer..'+output);
        return output;
    };

    var parsePrimerJson = function (script) {
        var tokens = tokenizePrimer(script)
            , i = tokens.length, token, output = { summary: "", title: "" };
        while (i--) {
            token = tokens[i];
            token.text = inline.lexer(token.text);
            switch (token.type) {
                case 'body': output.summary += token.text ? (token.text + "\n\n") : ""; break;
                case 'title': output.title = token.text ? (token.text) : ""; break;
            }
        }
        return output;
    };

    /**actor **/

    var tokenizeActor = function (script) {
        var src = script.split(regex.splitter)
            , i = src.length, line, match
            , tokens = [];

        while (i--) {
            line = src[i];
            // centered
            if (match = line.match(regex.centered)) {
                tokens.push({ type: 'centered', text: match[0].replace(/>|</g, '') });
                continue;
            }
            // actor_summary
            //   console.log('line..'+JSON.stringify(line));
            var actorRegex = "^(.+)(?:\n?)(.*)(?:\n?)(" + $L("头像") + "[:：](.+))?";
            if (match = line.match(new RegExp(actorRegex))) {
                //   console.log('match..'+JSON.stringify(match));
                if (match[2]) {
                    var text = match[2].split(/\|{2,}/g);
                    _.each(text.reverse(), function (k) {
                        tokens.push({ type: 'actor_description', text: k });
                    });
                }
                if (match[4]) {
                    tokens.push({ type: 'actor_picture', text: match[4] });
                }
                tokens.push({ type: 'actor_summary', text: match[1] });
                continue;
            }
            // line breaks
            if (regex.line_break.test(line)) {
                tokens.push({ type: 'line_break' });
                continue;
            }
            tokens.push({ type: 'action', text: line });
        }
        return tokens;
    };

    var parseActor = function (script) {
        var tokens = tokenizeActor(script)
            , i = tokens.length, token, html = [], output;
        html.push('<div class=\"charpter\">');
        html.push('<p class=\"charpter-title\">' + $L('人物简介') + '</p>');
        while (i--) {
            token = tokens[i];
            token.text = inline.lexer(token.text);
            switch (token.type) {
                case 'actor_summary': html.push('<p class=\"actor_summary\">' + token.text + '</p>'); break;
                case 'actor_description': html.push('<p class=\"actor_description\">' + token.text + '</p>'); break;
                case 'actor_picture': html.push('<p class=\"actor_picture\"><img src=\"' + token.text + '\" /></p>'); break;
            }
        }
        html.push('</div>');
        output = html.join('');
        //  console.log(output);
        return output;
    };

    /**body **/

    var tokenizeBody = function (script, booktype) {
        // console.log('booktype..'+booktype);
        if (!booktype) {
            booktype = "novel";
        }
        var src = lexer(script).split(booktype == 'juben' ? regex.splitter : regex.splitterBody)
            , i = src.length, line, match, parts, text, meta, x, xlen, parts2, dual
            , tokens = [];

        var blockregex = $L("@@(第(#{ddd})([场|节|聊]) *(.*))");
        var blockregex = ciiat.g11n.replaceWithTemp(blockregex, {
            ddd: "\\d*"
        });
        while (i--) {
            line = src[i];
            // scene headings
            var match = "";
            if (match = line.match(blockregex)) {
                text = match[0];
                // console.log('......match..'+match);
                if (match[3] == $L('场')) {
                    tokens.push({ type: 'scene', text: match[1].toUpperCase(), block_number: match[2] || undefined, title: match[4].toUpperCase() || undefined });
                }
                else if (match[2] == $L('场')) {
                    tokens.push({ type: 'scene', text: match[1].toUpperCase(), block_number: match[3] || undefined, title: match[4].toUpperCase() || undefined });
                }
                else if (match[3] == $L('聊')) {
                    if (match[4]) {
                        tokens.push({ type: 'timeline', text: match[1].toUpperCase(), block_number: match[2] || undefined, title: match[4].toUpperCase() || undefined });
                    }
                }
                else if (match[2] == $L('聊')) {
                    if (match[4]) {
                        tokens.push({ type: 'timeline', text: match[1].toUpperCase(), block_number: match[3] || undefined, title: match[4].toUpperCase() || undefined });
                    }
                }
                else {
                    tokens.push({ type: 'block', text: match[1], block_number: match[2] || undefined, title: match[4] || undefined });
                }
                continue;
            }
            if (booktype == 'chat') {
                if (match = line.match(regex.dialogue)) {
                    //   console.log(JSON.stringify(match));
                    // we're iterating from the bottom up, so we need to push these backwards
                    tokens.push({ type: 'dialogue_end' });
                    parts = match[3].split(/^[（\(](.+)\|(.+)[）\)]/);
                    // console.log('parts..'+JSON.stringify(parts));
                    if (parts.length == 4) {
                        /**
                        parts2 = parts[3].split(/(\{{2}.+\}{2})/).reverse();
                        for (x = 0, xlen = parts2.length; x < xlen; x++) {	
                          text = parts2[x];
                          if (text.length > 0) {
                             //  console.log('text..'+JSON.stringify(text,null,2));
                             var matchxx;
                             if (matchxx = text.match(/\{{2}(.+)\}{2}/)) {
                                 // console.log(JSON.stringify(matchxx,null,2));
                                  //tokens.push({ type: 'dpicture', text: matchxx[1] });
                              }
                              else{
                                   text=text.replace(/[“”\"]/g,"");
                                   tokens.push({ type: 'dialogue', text: text });
                              }
                          }
                        }**/
                        //console.log('tokens..'+JSON.stringify(tokens));
                        //console.log('role..'+parts[2]);
                        tokens.push({ type: 'dialogue', text: parts[3].replace(/\{{2}/g, '<img src=').replace(/\}{2}/g, '>').trim() });
                        tokens.push({ type: 'cpicture', text: parts[1] });
                        tokens.push({ type: 'character', text: match[1].replace(/[:：]/g, '').trim().toUpperCase() });
                        tokens.push({ type: 'dialogue_begin', dual: (parts[2] == $L("主角") ? 'right' : 'left') });
                        // console.log('tokens..'+JSON.stringify(tokens));
                    }
                    else {
                        tokens.push({ type: 'dialogue', text: line });
                    }
                    continue;
                }
                else {
                    //  console.log(line);
                    tokens.push({ type: 'action', text: line });
                    continue;
                }
            }
            else {
                // centered
                if (match = line.match(regex.centered)) {
                    tokens.push({ type: 'centered', text: match[0].replace(/>|</g, '') });
                    continue;
                }
                // picture
                if (match = line.match(new RegExp("^" + $L("插图") + "[:：](\\(\(\\S+)\\))?(.*)"))) {
                    tokens.push({ type: 'picture', text: match[3], size: (match[2] ? match[2] : null) });
                    continue;
                }
                switch (booktype) {
                    case "script":
                    case "juben":
                        // transitions
                        var transitionregex = $L("^(?:.*)((淡入|切至|切回|淡出)[:：])");
                        if (match = line.match(new RegExp(transitionregex))) {
                            tokens.push({ type: 'transition', text: match[1] || match[2] });
                            continue;
                        }
                        // dialogue blocks - characters, parentheticals and dialogue
                        // console.log(JSON.stringify('line..'+line));
                        if (match = line.match(regex.dialogue)) {
                            //  console.log(JSON.stringify(match));
                            // we're iterating from the bottom up, so we need to push these backwards
                            if (match[2]) {
                                tokens.push({ type: 'dual_dialogue_end' });
                            }

                            tokens.push({ type: 'dialogue_end' });
                            parts = match[3].split(/([（\(].+[）\)])/).reverse();
                            //console.log('parts..'+JSON.stringify(parts));
                            for (x = 0, xlen = parts.length; x < xlen; x++) {
                                text = parts[x];
                                if (text.length > 0) {
                                    tokens.push({ type: regex.parenthetical.test(text) ? 'parenthetical' : 'dialogue', text: text.replace(/[“”\"\n]/g, "") });
                                }
                            }
                            // console.log('tokens..'+JSON.stringify(tokens));
                            tokens.push({ type: 'character', text: match[1].replace(/[:：]/g, '').trim().toUpperCase() });
                            tokens.push({ type: 'dialogue_begin', dual: match[2] ? 'right' : dual ? 'left' : undefined });
                            if (dual) {
                                tokens.push({ type: 'dual_dialogue_begin' });
                            }

                            dual = match[2] ? true : false;
                            continue;
                        }

                        break;
                    default:
                        break;
                }
                // notes
                if (match = line.match(regex.note)) {
                    tokens.push({ type: 'note', text: match[1] });
                    continue;
                }



                // page breaks
                if (regex.page_break.test(line)) {
                    tokens.push({ type: 'page_break' });
                    continue;
                }

                // line breaks
                if (regex.line_break.test(line)) {
                    tokens.push({ type: 'line_break' });
                    continue;
                }

                tokens.push({ type: 'action', text: line });
            }
        }
        return tokens;
    };

    var tokenizeSpd = function (script) {
        var src = lexer(script).split(regex.splitter)
            , i = src.length, line, match, parts, text, meta, x, xlen, dual
            , tokens = [];
        while (i--) {
            line = src[i];
            // scene headings
            var match = "";
            // console.log(JSON.stringify('line..'+line));
            if (match = line.match(regex.dialogue)) {
                //  console.log(JSON.stringify(match));
                // we're iterating from the bottom up, so we need to push these backwards
                if (match[2]) {
                    tokens.push({ type: 'dual_dialogue_end' });
                }

                tokens.push({ type: 'dialogue_end' });
                parts = match[3].split(/([（\(].+[）\)])/).reverse();
                //console.log('parts..'+JSON.stringify(parts));
                for (x = 0, xlen = parts.length; x < xlen; x++) {
                    text = parts[x];

                    if (text.length > 0) {
                        tokens.push({ type: regex.parenthetical.test(text) ? 'parenthetical' : 'dialogue', text: text });
                    }
                }
                // console.log('tokens..'+JSON.stringify(tokens));
                tokens.push({ type: 'character', text: match[1].replace(/[:：]/g, '').trim() });
                tokens.push({ type: 'dialogue_begin', dual: match[2] ? 'right' : dual ? 'left' : undefined });
                if (dual) {
                    tokens.push({ type: 'dual_dialogue_begin' });
                }

                dual = match[2] ? true : false;
                continue;
            }
        }
    };

    var parseBody = function (script, title, withInfo, chapterdisplay, partdisplay, booktype) {
        var tokens = tokenizeBody(script, booktype)
            , i = tokens.length, token
            , title, title_page = [], title_note = [], html = [], output;
        if (withInfo) {
            if (!title) {
                html.push('<div class=\"charpter\">');
                html.push('<p class=\"charpter-title\">' + $L("正文") + '</p>');
                html.push('</div>');
            }
            else {
                var match = "";
                var taxonomyregex = $L("(第#{ddd}([章|卷|集])) *(.*)");
                var temp = ciiat.g11n.replaceWithTemp(taxonomyregex, {
                    ddd: "\\d*"
                });

                if (match = title.match(new RegExp(temp))) {
                    html.push('<div class=\"charpter\">');
                    if (match[2] == $L("卷")) {
                        switch (booktype) {
                            case "script":
                            case "juben":
                            case "chat":
                                html.push('<p class=\"charpter-title' + (match[2] == $L("卷") ? " volume" : "") + '\" >' + (match[3] ? match[3] : "") + '</p>');
                                break;
                            default:
                                html.push('<p class=\"charpter-title' + (match[2] == $L("卷") ? " volume" : "") + '\" >' + title + '</p>');
                                break;
                        }
                    }
                    else {
                        switch (chapterdisplay) {
                            case "call":
                                html.push('<p class=\"charpter-title' + (match[2] == $L("卷") ? " volume" : "") + '\" >' + title + '</p>');
                                break;
                            case "cnumber":
                                html.push('<p class=\"charpter-title' + (match[2] == $L("卷") ? " volume" : "") + '\" >' + (match[1] ? match[1] : "") + '</p>');
                                break;
                            case "ctitle":
                                html.push('<p class=\"charpter-title' + (match[2] == $L("卷") ? " volume" : "") + '\" >' + (match[3] ? match[3] : "") + '</p>');
                                break;
                            default:
                                break;
                        }
                    }
                    html.push('</div>');
                }
            }
        }
        while (i--) {
            token = tokens[i];
            token.text = inline.lexer(token.text);
            switch (token.type) {
                case 'block':
                    switch (partdisplay) {
                        case "pall":
                            html.push('<p class=\"block\" ' + (token.block_number ? ' bid=\"' + token.block_number + '\">' : '>') + token.block_number + " " + (token.title ? token.title : "") + '</p>');
                            break;
                        case "pnumber":
                            html.push('<p class=\"block\" ' + (token.block_number ? ' bid=\"' + token.block_number + '\">' : '>') + token.block_number + '</p>');
                            break;
                        case "ptitle":
                            html.push('<p class=\"block\" ' + (token.block_number ? ' bid=\"' + token.block_number + '\">' : '>') + (token.title ? token.title : "") + '</p>');
                            break;
                        default:
                            break;
                    }
                    break;
                case 'scene':
                    html.push('<p class=\"scene\" ' + (token.block_number ? ' bid=\"' + token.block_number + '\">' : '>') + (token.title ? token.title : "") + ' <span class=\"scenenum\" >' + token.block_number + '</span></p>');
                    break;
                case 'timeline':
                    html.push('<p class=\"timeline\" ' + (token.block_number ? ' bid=\"' + token.block_number + '\"><span>' : '><span>') + (token.title ? token.title : "") + '</span></p>');
                    break;
                case 'transition': html.push((token.text.indexOf($L('淡出')) == 0 || token.text.indexOf($L('切至')) == 0) ? ('<p class=\"transition\">' + token.text + '</p>') : '<p class=\"transition left-aligned\">' + token.text + '</p>'); break;
                case 'dual_dialogue_begin': html.push('<div class=\"dual-dialogue\">'); break;
                case 'dialogue_begin': html.push('<div class=\"dialogue' + (token.dual ? ' ' + token.dual : '') + '\">'); break;
                case 'character':
                    html.push('<p class=\"character\">' + token.text + '</p>');
                    break;
                case "cpicture":
                    html.push('<p class=\"cpicture\"><img src=\"' + token.text + '\" /></p>');
                    break;
                case 'parenthetical': html.push('<p class=\"parenthetical\">' + token.text + '</p>'); break;
                case 'dialogue':
                    html.push('<p class=\"dialoguecontent\">' + token.text + '</p>');
                    break;
                case 'dialogue_end': html.push('</div> '); break;
                case 'dual_dialogue_end': html.push('</div> '); break;
                case 'dpicture': html.push('<p class=\"dialoguecontent\"><img onclick=\"window.open(\'' + (token.text ? token.text : "") + '\',\'_blank\');\" src=\"' + (token.text ? token.text : "") + '\" /></p>'); break;
                case 'picture':
                    if (token.size) {
                        if (token.size == $L("大")) {
                            html.push('<p class=\"picture\"><img src=\"' + (token.text ? token.text : "") + '\" width=\"440\" height=\"330\" /></p>');
                        }
                        else if (token.size == $L("小")) {
                            html.push('<p class=\"picture\"><img src=\"' + (token.text ? token.text : "") + '\"  width=\"440\" height=\"110\"/></p>');
                        }
                        else {
                            html.push('<p class=\"picture\"><img src=\"' + (token.text ? token.text : "") + '\" width=\"440\" height=\"220\" /></p>');
                        }
                    }
                    else {
                        html.push('<p class=\"picture\"><img src=\"' + (token.text ? token.text : "") + '\" /></p>');
                    }
                    break;
                case 'synopsis': html.push('<p class=\"synopsis\">' + token.text + '</p>'); break;
                case 'note': html.push('<!-- ' + token.text + '-->'); break;
                case 'action': html.push('<p class=\"action\">' + (token.text ? token.text : "") + '</p>'); break;
                case 'centered': html.push('<p class=\"centered\">' + token.text + '</p>'); break;
                case 'page_break': html.push('<hr />'); break;
                case 'line_break': html.push('<br />'); break;
            }
        }
        output = html.join('');
        //console.log('output..'+output);
        return output;
    };
    var parseBodyWord = function (script, withInfo, booktype) {
        var tokens = tokenizeBody(script, booktype)
            , i = tokens.length, token
            , title, title_page = [], title_note = [], html = [], output;
        if (withInfo) {
            html.push('<h2>' + $L('正文') + '</h2>');
        }
        while (i--) {
            token = tokens[i];
            // token.text = inline.lexer(token.text);
            //console.log(JSON.stringify(token));
            token.text = inline2.lexer(token.text);
            switch (token.type) {
                case 'block': html.push('<h4>' + token.block_number + " " + token.text + '</h4>'); break;
                case 'scene': html.push('<h4>' + token.block_number + " " + token.text + '</h4>'); break;
                case 'synopsis': html.push('<p>' + token.text + '</p>'); break;
                case 'parenthetical': html.push('<p style="text-align:center">' + token.text + '</p>'); break;
                case 'character': html.push('<h4 style="text-align:center">' + token.text + '</h4>'); break;
                case 'dialogue':
                    switch (booktype) {
                        case "script":
                        case "juben":
                            html.push('<p style="margin-left:20%;margin-right:20%;text-align:left">' + token.text + '</p>'); break;
                        default:
                            html.push('<p style="text-align:left">' + token.text + '</p>'); break;
                    }
                    break;
                case 'action':
                    switch (booktype) {
                        case "script":
                        case "juben":
                        case "chat":
                            html.push('<p>' + (token.text ? token.text : "") + '</p>'); break;
                            break;
                        default:
                            html.push('<p style="text-indent: 2em;">' + (token.text ? token.text : "") + '</p>'); break;
                            break;
                    }
                    break;
                case 'timeline':
                    html.push('<p style="text-align:center">' + (token.title ? token.title : "") + '</p>');
                    break;
                case 'character':
                    html.push('<p style="text-align:left:float:left">' + token.text + '</p>');
                    break;
                case 'centered': html.push('<p style="text-align:center">' + token.text + '</p>'); break;
                case 'picture': html.push('<p style="text-align:center"><img src=\"' + token.text + '\" /></p>'); break;
                case 'dpicture': html.push('<p style="margin-left:20%;margin-right:20%;text-align:left"><img src=\"' + token.text + '\" /></p>'); break;
                case 'page_break': html.push('<br>'); break;
                case 'line_break': html.push('<br>'); break;
            }
        }
        var output = html.join('');
        //console.log('output..'+output);
        return output;
    };
    var parseBodyWord2 = function (script) {
        var tokens = tokenizeBody(script)
            , i = tokens.length, token
            , title, title_page = [], title_note = [], html = [], output;
        var actions = [];
        while (i--) {
            token = tokens[i];
            var action = {};
            // token.text = inline.lexer(token.text);
            switch (token.type) {
                case 'centered':
                    action = { type: "parenthetical", text: "" };
                    break;
                case 'synopsis':
                    action = { type: "action", text: "" };
                    break;
                case 'page_break':
                case 'line_break':
                    action = { type: "break", text: "" };
                    break;
                default:
                    action = { type: token.type, text: token.text };
                    break;
            }
            actions.push(action);
        }
        return actions;
    };
    /**refer **/

    var tokenizeRefer = function (script) {
        var src = script.split(regex.splitterBody)
            , i = src.length, line, match
            , tokens = [], summary = [];

        while (i--) {
            line = src[i];
            // centered
            if (match = line.match(regex.centered)) {
                tokens.push({ type: 'centered', text: match[0].replace(/>|</g, '') });
                continue;
            }
            summary.unshift(line);
        }
        tokens.unshift({ type: 'refer', text: summary.join('\n\n') });
        return tokens;
    };

    var parseRefer = function (script) {
        var tokens = tokenizeRefer(script)
            , i = tokens.length, token, html = [], output;
        html.push('<div class=\"charpter\">');
        html.push('<p class=\"charpter-title\">' + $L("附注") + '</p>');
        while (i--) {
            token = tokens[i];
            token.text = inline.lexer(token.text);
            switch (token.type) {
                case 'refer': html.push('<p class=\"refer\">' + token.text + '</p>'); break;
            }
        }
        html.push('</div>');
        output = html.join('');
        return output;
    };




    var inline = {
        note: '<!-- $1 -->',

        line_break: '<br />',

        bold_italic_underline: '<span class=\"bold italic underline\">$2</span>',
        bold_underline: '<span class=\"bold underline\">$2</span>',
        italic_underline: '<span class=\"italic underline\">$2</span>',
        bold_italic: '<span class=\"bold italic\">$2</span>',
        bold: '<span class=\"bold\">$2</span>',
        italic: '<span class=\"italic\">$2</span>',
        underline: '<span class=\"underline\">$2</span>'
    };

    inline.lexer = function (s) {
        if (!s) {
            return;
        }

        var styles = ['underline', 'italic', 'bold', 'bold_italic', 'italic_underline', 'bold_underline', 'bold_italic_underline']
            , i = styles.length, style, match;

        s = s.replace(regex.note_inline, inline.note).replace(/\\\*/g, '[star]').replace(/\\_/g, '[underline]').replace(/\n/g, inline.line_break);

        // if (regex.emphasis.test(s)) {                         // this was causing only every other occurence of an emphasis syntax to be parsed
        while (i--) {
            style = styles[i];
            match = regex[style];

            if (match.test(s)) {
                s = s.replace(match, inline[style]);
            }
        }
        // }

        return s.replace(/\[star\]/g, '*').replace(/\[underline\]/g, '_').trim();
    };

    var inline2 = {
        note: '<!-- $1 -->',
        line_break: '<br>',
        bold_italic_underline: '<b><i><u>$2</u></i></b>',
        bold_underline: '<b><u>$2</u></b>',
        italic_underline: '<i><u>$2</i></u>',
        bold_italic: '<b><i>$2</i></b>',
        bold: '<b>$2</b>',
        italic: '<i>$2</i>',
        underline: '<u>$2</u>'
    };

    inline2.lexer = function (s) {
        if (!s) {
            return;
        }

        var styles = ['underline', 'italic', 'bold', 'bold_italic', 'italic_underline', 'bold_underline', 'bold_italic_underline']
            , i = styles.length, style, match;

        s = s.replace(regex.note_inline, inline2.note).replace(/\\\*/g, '[star]').replace(/\\_/g, '[underline]').replace(/\n/g, inline.line_break);

        // if (regex.emphasis.test(s)) {                         // this was causing only every other occurence of an emphasis syntax to be parsed
        while (i--) {
            style = styles[i];
            match = regex[style];

            if (match.test(s)) {
                s = s.replace(match, inline2[style]);
            }
        }
        // }

        return s.replace(/\[star\]/g, '*').replace(/\[underline\]/g, '_').trim();
    };


    var book = function (script, callback) {
        return book.parse(script, callback);
    };
    book.parseSimple = function (script, callback, style) {
        if (!style) {
            style = '';
        }
        var charpters = lexer(script).split(regex.charpter);
        if (charpters.length > 1) {
            while (charpters.length) {
                var c = charpters.shift();
                switch (c) {
                    case $L("封面"):
                    case $L("封页"):
                        var body = charpters.shift();
                        var simple = {};
                        var tokens = tokenizeCover(body);
                        _.each(tokens, function (t) {
                            if (t.type == 'cover_name') {
                                simple.name = t.text;
                            }
                            else if (t.type == 'cover_author') {
                                simple.author = t.text;
                            }
                            else if (t.type == 'cover_source') {
                                simple.source = t.text;
                            }
                            else if (t.type == 'cover_type') {
                                simple.type = t.text.split(regex.keyvalue)[2];
                                if (simple.type.indexOf(" ") >= 0) {
                                    simple.type = simple.type.split(" ")[0];
                                }
                            }
                            else if (t.type == 'cover_booktype') {
                                var bt = t.text.split(regex.keyvalue)[2];
                                switch (bt) {
                                    case $L("聊小说"):
                                        simple.booktype = "chat";
                                        break;
                                    case $L("剧本"):
                                        simple.booktype = "script";
                                        break;
                                    case $L("脚本"):
                                        simple.booktype = "juben";
                                        break;
                                    case $L("电子书"):
                                        simple.booktype = "ebook";
                                        break;
                                    default:
                                        simple.booktype = "novel";
                                        break;
                                }
                            }
                            else if (t.type == 'cover_date') {
                                simple.updated = t.text.split(regex.keyvalue)[2];
                            }
                            else if (t.type == 'cover_status') {
                                simple.status = t.text.split(regex.keyvalue)[2];
                            }
                            else if (t.type == 'cover_size') {
                                simple.size = t.text.split(regex.keyvalue)[2];
                            }
                            else if (t.type == 'cover_style') {
                                simple.style = t.text.split(regex.keyvalue)[2];
                            }
                            else if (t.type == 'cover_copyright') {
                                simple.copyright = t.text.split(regex.keyvalue)[2];
                            }
                            else if (t.type == 'cover_chapterdisplay') {
                                var name = t.text.split(regex.keyvalue)[2];
                                switch (name) {
                                    case $L("章号与标题"):
                                        simple.chapterdisplay = "call";
                                        break;
                                    case $L("章号"):
                                        simple.chapterdisplay = "cnumber";
                                        break;
                                    case $L("标题"):
                                        simple.chapterdisplay = "ctitle";
                                        break;
                                    case $L("不显示"):
                                        simple.chapterdisplay = "none";
                                        break;
                                    default:
                                        simple.chapterdisplay = "call";
                                        break;
                                }
                            }
                            else if (t.type == 'cover_partdisplay') {
                                var name = t.text.split(regex.keyvalue)[2];
                                switch (name) {
                                    case $L("节号与标题"):
                                        simple.partdisplay = "pall";
                                        break;
                                    case $L("节号"):
                                        simple.partdisplay = "pnumber";
                                        break;
                                    case $L("标题"):
                                        simple.partdisplay = "ptitle";
                                        break;
                                    case $L("不显示"):
                                        simple.partdisplay = "pnone";
                                        break;
                                    default:
                                        simple.partdisplay = "pnumber";
                                        break;
                                }
                            }
                        })
                        //console.log('simple ..'+JSON.stringify(simple,null,2));
                        simple.body = script;
                        if (typeof callback === 'function') {
                            return callback(simple);
                        }
                        return simple;
                }
            }
        }
        return null;
    };
    book.parseJubenToSpd = function (script, callback, style) {
        //console.log(JSON.stringify(script));
        if (!style) {
            style = '';
        }
        var manageid = null;
        var sectionid = null;
        var book = {};
        var actors = [];
        var topics = [];
        var taxonomyregex = $L("(第#{ddd}([章|卷|集|场|节|聊])) *(.*)");
        taxonomyregex = ciiat.g11n.replaceWithTemp(taxonomyregex, {
            ddd: "\\d*"
        });
        var charpters = lexer(script).split(regex.spdcharpter);
        if (charpters.length > 1) {
            while (charpters.length) {
                var c = charpters.shift();
                var sectiontitle = c;
                var match = "";
                if (match = sectiontitle.match(new RegExp(taxonomyregex))) {
                    c = match[2];
                };
                switch (c) {
                    case $L("封面"):
                    case $L("封页"):
                        var body = charpters.shift();
                        var tokens = tokenizeCover(body);
                        _.each(tokens, function (t) {
                            // console.log(JSON.stringify(t));
                            if (t.type == 'cover_name') {
                                book.name = t.text;
                            }
                            else if (t.type == 'cover_author') {
                                book.author = t.text;
                            }
                            else if (t.type == 'cover_booktype') {
                                book.booktype = t.text.split(regex.keyvalue)[2];
                            }
                            else if (t.type == 'cover_type') {
                                book.type = t.text.split(regex.keyvalue)[2];
                                if (book.type.indexOf(" ") >= 0) {
                                    book.type = book.type.split(" ")[0];
                                }
                            }
                            else if (t.type == 'cover_status') {
                                book.status = t.text.split(regex.keyvalue)[2];
                            }
                            else if (t.type == 'cover_style') {
                                book.style = t.text.split(regex.keyvalue)[2];
                            }
                            else if (t.type == 'cover_chapterdisplay') {
                                book.chapterdisplay = t.text.split(regex.keyvalue)[2];
                            }
                            else if (t.type == 'cover_partdisplay') {
                                book.partdisplay = t.text.split(regex.keyvalue)[2];
                            }
                        });
                        break;
                    case $L("梗概"):
                        var body = charpters.shift();
                        var tokens = tokenizeSummary(body);
                        //  console.log("body.."+JSON.stringify(body));
                        _.each(tokens, function (t) {
                            if (t.type == 'shortsummary') {
                                book.shortsummary = t.text;
                            }
                            else if (t.type == 'summary') {
                                book.summary = t.text;
                            }
                        });
                        break;
                    case $L("附注"):
                        var body = charpters.shift();
                        var tokens = tokenizeRefer(body);
                        _.each(tokens, function (t) {
                            if (t.type == 'refer') {
                                book.note = t.text;
                            }
                        });
                        break;
                    case $L("序"):
                        var body = charpters.shift();
                        var output = parsePrimerJson(body);
                        if (output) {
                            output.type = "primer";
                            output.sort = -1000;
                            topics.push(output);
                        }
                        break;
                    case $L("卷"):
                        manageid = guid();
                        var body = charpters.shift();
                        var topic = {};
                        topic.type = "manage";
                        topic.summary = body;
                        topic.title = match[3] ? match[3] : "";
                        topic.id = manageid;
                        topics.push(topic);
                        break;
                    case $L("章"):
                    case $L("集"):
                        sectionid = guid();
                        var body = charpters.shift();
                        var topic = {};
                        topic.type = "section";
                        topic.summary = body;
                        topic.title = match[3] ? match[3] : "";
                        topic.parentid = manageid;
                        topic.id = sectionid;
                        topics.push(topic);
                        break;
                    case $L("节"):
                    case $L("场"):
                    case $L("聊"):
                        var body = charpters.shift();
                        var topic = {};
                        topic.type = "screen";
                        switch (book.booktype) {
                            case $L("聊小说"):
                                topic.body = body.replace(/([\(（].+[\)）])/g, "");
                                break;
                            default:
                                topic.body = body;
                                break;
                        }

                        if (!sectionid) {
                            topic.parentid = manageid;
                        }
                        else {
                            topic.parentid = sectionid;
                        }
                        topic.title = match[3] ? match[3] : "";
                        topics.push(topic);
                        break;
                    case $L("人物简介"):
                        var body = charpters.shift();
                        var tokens = tokenizeActor(body);
                        var top = 100;
                        var left = 50;
                        var actor = {};
                        _.each(tokens, function (t) {
                            // console.log(t.type+" "+t.text);
                            if (t.type == 'actor_description') {
                                actor = {};
                                actor.summary = t.text.replace(/(\|\|)+/g, "\n\n");
                            }
                            else if (t.type == 'actor_picture') {
                                actor.picture = t.text;
                            }
                            else if (t.type == 'actor_summary') {
                                var params = t.text.split("  ");
                                actor.y = top;
                                actor.name = params[0];
                                actor.x = left;
                                actor.age = params[1];
                                actor.sex = params[2];
                                actor.role = params[3];
                                actor.character = (params[4] ? params[4] : "") + (params[5] ? params[5] : "") + (params[6] ? params[6] : "");
                                //console.log(JSON.stringify(actor,null,2));
                                actors.push(actor);
                                left += 200;
                            }
                        });
                        //console.log(JSON.stringify(actors,null,2));
                        break;
                    default:
                        break;

                }
            }

        };
        var spd = {};
        spd.book = book;
        spd.actors = actors;
        spd.topics = topics;
        if (typeof callback === 'function') {
            return callback(spd);
        }
        // console.log(JSON.stringify(spd,null,2));
        return spd;
    };
    book.parseBodyWord = function (body, withInfo, booktype) {
        var output = parseBodyWord(body, withInfo, booktype);
        return output;
    };
    book.parseBodyWord2 = function (body) {
        var output = parseBodyWord2(body);
        return output;
    };
    book.parseSimpleSummary = function (script, callback, style) {
        if (!style) {
            style = '';
        }
        var charpters = lexer(script).split(regex.charpter);
        if (charpters.length > 1) {
            while (charpters.length) {
                var c = charpters.shift();
                switch (c) {
                    case $L("梗概"):
                        return '##' + $L("梗概") + '\n\n' + charpters.shift();
                }
            }
        }
        return null;
    };
    book.parseCharpters = function (script, callback, booktype, style, chapterdisplay, partdisplay) {
        //console.log(JSON.stringify('booktype..'+booktype));
        if (!script) {
            return "";
        }
        if (!booktype) {
            booktype = "novel";
        }
        if (!style) {
            style = '';
        }
        var match = "";
        var taxonomyregex = $L("(第#{ddd}([章|卷|集])) *(.*)");
        taxonomyregex = ciiat.g11n.replaceWithTemp(taxonomyregex, {
            ddd: "\\d*"
        });
        //console.log(script);
        var charpters = lexer(script).split(regex.charpter);
        var components = [];
        if (charpters.length > 1) {
            var id = 0;
            while (charpters.length) {
                var c = charpters.shift();
                var title = c;
                if (match = title.match(new RegExp(taxonomyregex))) {
                    //console.log(JSON.stringify(match));
                    if (match[2] == $L("卷")) {
                        c = $L("卷");
                    }
                    else if (match[2] == $L("集")) {
                        c = $L("集");
                    }
                    else {
                        c = $L("章");
                    }
                };
                //console.log("title.."+c);
                switch (c) {
                    case $L("封面"):
                    case $L("封页"):
                        var body = charpters.shift();
                        var output = "<div class='book " + style + "'>";
                        output += parseCover(body);
                        output += "</div>";
                        components.push({ id: "c" + id, title: c, content: output });
                        break;
                    case $L("梗概"):

                        var body = charpters.shift();
                        var output = "<div class='book " + style + "'>";
                        output += parseSummary(body);
                        output += "</div>";
                        components.push({ id: "c" + id, title: c, content: output });
                        break;
                    case $L("人物简介"):
                        var body = charpters.shift();
                        var output = "<div class='book " + style + "'>";
                        output += parseActor(body);
                        output += "</div>";
                        components.push({ id: "c" + id, title: c, content: output });
                        break;
                    case $L("附注"):
                        var body = charpters.shift();
                        var output = "<div class='book " + style + "'>";
                        output += parseRefer(body);
                        output += "</div>";
                        components.push({ id: "c" + id, title: c, content: output });
                        break;
                    case $L("序"):
                        var body = charpters.shift();
                        var output = "<div class='book " + booktype + " " + style + "'>";
                        output += parsePrimer(body);
                        output += "</div>";
                        components.push({ id: "c" + id, title: c, content: output });
                        break;
                    case $L("卷"):
                        var body = charpters.shift();
                        var output = "<div class='book " + booktype + " " + style + "'>";
                        output += parseBody(body, title, true, chapterdisplay, partdisplay, booktype);
                        output += "</div>";
                        switch (booktype) {
                            case "script":
                            case "juben":
                            case "chat":
                                components.push({ id: "c" + id, title: match[3], content: output });
                                break;
                            default:
                                components.push({ id: "c" + id, title: title, content: output });
                                break;
                        }
                        break;
                    case $L("正文"):
                    case $L("章"):
                    case $L("集"):
                        var body = charpters.shift();
                        var output = "<div class='book " + booktype + " " + style + "'>";
                        var content = parseBody(body, title, true, chapterdisplay, partdisplay, booktype);
                        if (content.length > 100) {
                            output += content;
                            output += "</div>";
                            components.push({ id: "c" + id, title: title, content: output });
                        }
                        break;
                }
                id++;
            }

        }
        else {
            output += parseBody(script, false, false, chapterdisplay, partdisplay, booktype);
            components.push({ id: 'all', content: output });
        }
        output += "</div>";
        if (typeof callback === 'function') {
            return callback(components);
        }

        return components;
    };
    book.parse = function (script, callback, booktype, bookstyle, chapterdisplay, partdisplay) {
        if (!script) {
            return "";
        }
        if (!booktype) {
            booktype = '';
        }
        if (!bookstyle) {
            bookstyle = '';
        }
        //console.log(script);
        var output = "<div class='book " + booktype + " " + bookstyle + "'>";
        var charpters = lexer(script).split(regex.charpter);
        if (charpters.length > 1) {

            while (charpters.length) {
                var c = charpters.shift();
                //console.log('charpter..'+c);
                if (c) {
                    switch (c) {
                        case $L("封面"):
                        case $L("封页"):
                            var body = charpters.shift();
                            output += parseCover(body);
                            break;
                        case $L("梗概"):
                            var body = charpters.shift();
                            output += parseSummary(body);
                            break;
                        case $L("人物简介"):
                            var body = charpters.shift();
                            output += parseActor(body);
                            break;
                        case $L("附注"):
                            var body = charpters.shift();
                            output += parseRefer(body);
                            break;
                        case $L("序"):
                            var body = charpters.shift();
                            output += parsePrimer(body);
                            break;
                        case $L("正文"):
                            var body = charpters.shift();
                            //console.log('body..'+body);
                            output += parseBody(body, null, true, chapterdisplay, partdisplay, booktype);
                            break;
                        default:
                            var body = charpters.shift();
                            output += parseBody(body, c, true, chapterdisplay, partdisplay, booktype);
                            break;
                    }
                }
            }

        }
        else {
            output += parseBody(script, null, false, chapterdisplay, partdisplay, booktype);
        }
        output += "</div>";
        //console.log(output);
        if (typeof callback === 'function') {
            return callback(output);
        }
        return output;
    };
    if (typeof module !== 'undefined') {
        module.exports = book;
    } else {
        this.book = book;
    }
}).call(this);

